package com.fastbee.sip.server.impl;

import com.fastbee.sip.domain.MediaServer;
import com.fastbee.sip.domain.SipConfig;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.enums.SessionType;
import com.fastbee.sip.model.VideoSessionInfo;
import com.fastbee.sip.server.ISipCmd;
import com.fastbee.sip.server.ReqMsgHeaderBuilder;
import com.fastbee.sip.server.VideoSessionManager;
import com.fastbee.sip.service.IMediaServerService;
import com.fastbee.sip.service.ISipCacheService;
import com.fastbee.sip.service.ISipConfigService;
import com.fastbee.sip.util.SipUtil;
import com.fastbee.sip.util.ZlmApiUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.sip.*;
import javax.sip.address.SipURI;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@Slf4j
@Component
public class SipCmdImpl implements ISipCmd {

    @Autowired
    private VideoSessionManager streamSession;

    @Autowired
    private ISipCacheService sipCacheService;

    @Autowired
    private ReqMsgHeaderBuilder headerBuilder;

    @Autowired
    private ISipConfigService sipConfigService;

    @Autowired
    private IMediaServerService mediaServerService;

    @Autowired
    private ZlmApiUtils zlmApiUtils;

    @Autowired
    @Qualifier(value = "udpSipServer")
    private SipProvider sipserver;

    @Override
    public void playStreamCmd(SipDevice device, String channelId, boolean record) {
        try {
            SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
            MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
            VideoSessionInfo info = new VideoSessionInfo();
            info.setDeviceId(device.getDeviceSipId());
            info.setChannelId(channelId);
            if (record) {
                info.setType(SessionType.playrecord);
            } else {
                info.setType(SessionType.play);
            }
            info.setStreamMode(device.getStreammode().toUpperCase());
            mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
            String content = buildRequestContent(sipConfig, mediaInfo, info);

            Request request = headerBuilder.createInviteRequest(device, channelId, content, null, "live",
                    null, info.getSsrc());

            ClientTransaction transaction = transmitRequest(request);
            info.setTransaction(transaction);
            streamSession.put(info.getStream(), info);

        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String playbackStreamCmd(SipDevice device, String channelId, String startTime, String endTime) {
        try {
            //默认获取admin账号配置
            SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
            MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
            VideoSessionInfo info = new VideoSessionInfo();
            info.setDeviceId(device.getDeviceSipId());
            info.setChannelId(channelId);
            info.setType(SessionType.playback);
            info.setStartTime(startTime);
            info.setEndTime(endTime);
            info.setStreamMode(device.getStreammode().toUpperCase());
            info.setViaTag(SipUtil.getNewViaTag());
            info.setFromTag(SipUtil.getNewFromTag());
            mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
            String content = buildRequestContent(sipConfig, mediaInfo, info);
            Request request = headerBuilder.createPlaybackInviteRequest(device, channelId, content, info.getViaTag(),
                    info.getFromTag(), null);
            ClientTransaction transaction = transmitRequest(request);
            info.setTransaction(transaction);
            info.setCallId(transaction.getDialog().getCallId());
            streamSession.put(info.getStream(), info);
            return info.getSsrc();
        } catch (SipException | ParseException | InvalidArgumentException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 请求历史媒体下载
     *
     * @param device        视频设备
     * @param channelId     预览通道
     * @param startTime     开始时间,格式要求：yyyy-MM-dd HH:mm:ss
     * @param endTime       结束时间,格式要求：yyyy-MM-dd HH:mm:ss
     * @param downloadSpeed 下载倍速参数
     */
    @Override
    public String downloadStreamCmd(SipDevice device, String channelId,
                                  String startTime, String endTime, int downloadSpeed) {
        try {
            SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
            MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
            VideoSessionInfo info = new VideoSessionInfo();
            info.setDeviceId(device.getDeviceSipId());
            info.setChannelId(channelId);
            info.setType(SessionType.download);
            info.setStartTime(startTime);
            info.setEndTime(endTime);
            info.setDownloadSpeed(downloadSpeed);
            info.setStreamMode(device.getStreammode().toUpperCase());
            mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);

            String content = buildRequestContent(sipConfig, mediaInfo, info);

            log.debug("此时请求下载信令的ssrc===>{}", info.getSsrc());
            Request request = headerBuilder.createPlaybackInviteRequest(device, channelId, content.toString(), null,
                    "download", null);
            ClientTransaction transaction = transmitRequest(request);
            info.setTransaction(transaction);
            streamSession.put(info.getStream(), info);
            return info.getSsrc();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }


    @Override
    public void streamByeCmd(String deviceId, String streamId) {
        try {
            MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(deviceId);
            VideoSessionInfo sinfo = streamSession.get(streamId);
            if (sinfo == null) {
                sipCacheService.stopStream(streamId);
            } else {
                ClientTransaction transaction = sinfo.getTransaction();
                if (transaction == null) {
                    return;
                }
                Dialog dialog = transaction.getDialog();
                if (dialog == null) {
                    return;
                }
                Request byeRequest = dialog.createRequest(Request.BYE);
                SipURI byeURI = (SipURI) byeRequest.getRequestURI();
                String vh = transaction.getRequest().getHeader(ViaHeader.NAME).toString();
                Pattern p = Pattern.compile("(\\d+\\.\\d+\\.\\d+\\.\\d+)\\:(\\d+)");
                Matcher matcher = p.matcher(vh);
                if (matcher.find()) {
                    String ip = matcher.group(1);
                    byeURI.setHost(ip);
                    String port = matcher.group(2);
                    byeURI.setPort(Integer.parseInt(port));
                }
                ClientTransaction clientTransaction = sipserver.getNewClientTransaction(byeRequest);
                dialog.sendRequest(clientTransaction);
                VideoSessionManager.CSEQCACHE.remove(streamId);
                log.info("streamByeCmd:{}", byeRequest);
                streamSession.remove(streamId);
                //关闭流媒体服务器推流端口
                if (mediaInfo != null) {
                    zlmApiUtils.closeRTPServer(mediaInfo, streamId);
                }
            }
        } catch (ParseException | SipException e) {
            e.printStackTrace();
        }
    }


    private ClientTransaction transmitRequest(Request request) throws SipException {
        log.info("transmitRequest:{}", request);
        ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
        clientTransaction.sendRequest();
        return clientTransaction;
    }

    private String buildRequestContent(SipConfig sipConfig, MediaServer mediaInfo, VideoSessionInfo info) {
        String streamMode = info.getStreamMode();
        StringBuilder content = new StringBuilder(200);
        content.append("v=0\r\n");
        switch (info.getType()) {
            case play:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Play\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=0 0\r\n");
                break;
            case playrecord:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Play\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=0 0\r\n");
                break;
            case playback:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Playback\r\n");
                content.append("u=").append(info.getChannelId()).append(":0\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=").append(info.getStartTime()).append(" ").append(info.getEndTime()).append("\r\n");
                break;
            case download:
                content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("s=Download\r\n");
                content.append("u=").append(info.getChannelId()).append(":0\r\n");
                content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
                content.append("t=").append(info.getStartTime()).append(" ").append(info.getEndTime()).append("\r\n");
                break;
        }
        if (sipConfig.getSeniorsdp() != null && sipConfig.getSeniorsdp() == 1) {
            if ("TCP-PASSIVE".equals(streamMode)) {
                content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
            } else if ("TCP-ACTIVE".equals(streamMode)) {
                content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
            } else if ("UDP".equals(streamMode)) {
                content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 126 125 99 34 98 97\r\n");
            }
            content.append("a=recvonly\r\n");
            content.append("a=rtpmap:96 PS/90000\r\n");
            content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
            content.append("a=rtpmap:126 H264/90000\r\n");
            content.append("a=rtpmap:125 H264S/90000\r\n");
            content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
            content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
            content.append("a=fmtp:99 profile-level-id=3\r\n");
            content.append("a=rtpmap:98 H264/90000\r\n");
            content.append("a=rtpmap:97 MPEG4/90000\r\n");
            if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
                content.append("a=setup:passive\r\n");
                content.append("a=connection:new\r\n");
            } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
                content.append("a=setup:active\r\n");
                content.append("a=connection:new\r\n");
            }
        } else {
            switch (streamMode) {
                case "TCP-PASSIVE":
                    content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 97 98 99\r\n");
                    break;
                case "TCP-ACTIVE":
                    content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 97 98 99\r\n");
                    break;
                case "UDP":
                    //content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 97 98 99\r\n");
                    content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 97 98\r\n");
                    break;
            }
            content.append("a=recvonly\r\n");
            content.append("a=rtpmap:96 PS/90000\r\n");
            content.append("a=rtpmap:97 MPEG4/90000\r\n");
            content.append("a=rtpmap:98 H264/90000\r\n");
            //content.append("a=rtpmap:99 H265/90000\r\n");
            if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
                content.append("a=setup:passive\r\n");
                content.append("a=connection:new\r\n");
            } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
                content.append("a=setup:active\r\n");
                content.append("a=connection:new\r\n");
            }
        }
        if (info.getType() == SessionType.download) {
            content.append("a=downloadspeed:").append(info.getDownloadSpeed()).append("\r\n");
        }
        content.append("y=").append(info.getSsrc()).append("\r\n");// ssrc
        return content.toString();
    }
}
